﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PhoneStore.Entity;
using System.Data.Objects.DataClasses;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data;

namespace PhoneStore.DataAccess
{
    public class BaseDAO
    {
        protected void DoTransaction(Action<DienThoaiEntities> action)
        {
            using (var dtx = new DienThoaiEntities())
            {
                action(dtx);
                dtx.SaveChanges();                
            }
        }

        protected T DoQuery<T>(Func<DienThoaiEntities, T> func)
        {
            using (var dtx = new DienThoaiEntities())
            {
                return func(dtx);
            }
        }

        public void Add<T>(T obj) where T:EntityObject
        {
            DoTransaction(x =>x.AddObject(x.MetadataWorkspace.
                GetEntityContainer(x.DefaultContainerName, System.Data.Metadata.Edm.DataSpace.CSpace).
                BaseEntitySets.Where(bes => bes.ElementType.Name == typeof(T).Name).FirstOrDefault().Name
                ,obj));
        }

        public void Update<T>(T obj) where T : EntityObject
        {
            Phone d = new Phone();
            DoTransaction(ctx =>
                {
                    ctx.Attach(obj);
                    ctx.ObjectStateManager.ChangeObjectState(obj, System.Data.EntityState.Modified);
                    Type t = typeof(T);
                    var NavProps = t.GetProperties().Where(x => x.PropertyType.IsGenericType &&
                        x.PropertyType.GetGenericTypeDefinition() == 
                        typeof(System.Data.Objects.DataClasses.EntityReference<>));
                    foreach (var i in NavProps)
                    {
                        var refProp = i.GetValue(obj, null);
                        var prop = refProp.GetType().GetProperty("Value")
                            .GetValue(refProp, null) as System.Data.Objects.DataClasses.IEntityWithKey;
                        if (prop != null)
                        {
                            ctx.Attach(prop);
                            ctx.ObjectStateManager.ChangeObjectState(prop, System.Data.EntityState.Modified);
                        }
                    }
                }
            );
        }

        public void Delete<T>(T obj) where T : EntityObject
        {
            DoTransaction(x =>
                {
                    x.Attach(obj);
                    x.DeleteObject(obj);
                });
        }

        public IList<T> GetAll<T>(params string[] includeList) where T : EntityObject
        {
            return DoQuery(x => SetInclude(x.GetObjectSet<T>(), includeList).ToList());
        }

        public IList<T> GetAll<T>(Expression<Func<T,bool>> exp,params string[] includeList) where T : EntityObject
        {
            return DoQuery(x => SetInclude(x.GetObjectSet<T>(), includeList).Where(exp).ToList());
        }

        public T Single<T>(Expression<Func<T, bool>> exp,params string[] includeList) where T : EntityObject
        {
            return DoQuery(x => SetInclude(x.GetObjectSet<T>(),includeList).SingleOrDefault(exp));
        }

        public IList<string> GetPhonePropertyValues(Expression<Func<Phone, string>> exp)
        {
            return DoQuery(x => x.Phones.Select(exp).Distinct().ToList());
        }

        private ObjectQuery<T> SetInclude<T>(ObjectQuery<T> objSet, string[] includeList)
        {
            if (includeList != null)
            {
                foreach (var inc in includeList)
                {
                    objSet = objSet.Include(inc);
                }
            }
            return objSet;
        }

    }
}
